`include "mycpu.h"

module exe_stage(
    input                          clk           ,
    input                          reset         ,
    input                          flush         ,
    input                          ms_flush      ,
    input                          ms_inst_mtc0  ,
    //allowin
    input                          ms_allowin    ,
    output                         es_allowin    ,
    //from ds
    input                          ds_to_es_valid,
    input  [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus  ,
    //to ms
    output                         es_to_ms_valid,
    output [`ES_TO_MS_BUS_WD -1:0] es_to_ms_bus  ,
    //to ds
    output                         es_to_ds_valid,
    output [`ES_TO_DS_BUS_WD -1:0] es_to_ds_bus,
    //tlb
    input tlb_found,
    input tlb_valid,
    input tlb_dirty,
    output ex_store,
    // data sram interface
    output        data_sram_req,
    output        data_sram_wr,
    output [1:0]  data_sram_size,
    output [3:0]  data_sram_wstrb,
    output [31:0] data_sram_addr,
    output [31:0] data_sram_wdata,
    input         data_sram_addr_ok,
    output [63:0] mulresult,
    output op_tlbp
);

reg         es_valid      ;
wire        es_ready_go   ;

reg  [`DS_TO_ES_BUS_WD -1:0] ds_to_es_bus_r;
wire [11:0] es_alu_op     ;
wire        es_load_op    ;
wire        es_src1_is_sa ;  
wire        es_src1_is_pc ;
wire        es_src2_is_imm; 
wire        es_src2_is_0imm;
wire        es_src2_is_8  ;
wire        es_gr_we      ;
wire        es_mem_we     ;
wire [ 1:0] addr_4        ;
wire [ 4:0] es_dest       ;
wire [15:0] es_imm        ;
wire [31:0] es_rs_value   ;
wire [31:0] es_rt_value   ;
wire [31:0] es_pc         ;

wire    [31:0] divoutlo,divouthi;
wire    divcomplete;

wire        es_inst_lwl;
wire        es_inst_lwr;
wire        es_inst_swl;
wire        es_inst_swr;
wire        es_load_sign;
wire        es_load_byte;
wire        es_load_hw;
wire        es_store_byte;
wire        es_store_hw;

wire    inst_div,
         inst_divu,
         inst_mult,
         inst_multu,
         inst_mfhi,
         inst_mflo,
         inst_mthi,
         inst_mtlo;

wire ds_ex;
wire [4:0]ds_excode;
wire [4:0] es_excode;
wire es_ex,es_bd;
wire es_inst_mfc0,es_inst_mtc0,es_inst_eret;
wire [4:0]es_c0_waddr;
wire es_inst_tlbwi;
wire es_inst_tlbr;
wire es_src1_is_0;

wire pre_overflow;
wire inst_lw,inst_sw,inst_lh,inst_lhu,inst_sh;
wire [31:0] fs_badvaddr;
wire refetch;

assign {es_refetch,//212
        ds_tlb_r,//211
        op_tlbp,//210
        es_inst_tlbwi,//209
        es_inst_tlbr,//208
        inst_lw, //207
        inst_sw,
        inst_lh,
        inst_lhu,
        inst_sh,   
        pre_overflow,   //202
        fs_badvaddr,  //   201  :170
        es_c0_waddr,  //169:165
        es_bd,       //
        ds_ex,          //
        ds_excode,    // 162:158
        es_inst_mfc0, //
        es_inst_mtc0, //
        es_inst_eret,//155
        inst_div, //154
        inst_divu, //
        inst_mult, //
        inst_multu, //
        inst_mfhi, // 150
        inst_mflo, //
        inst_mthi, //
        inst_mtlo, //
        es_inst_lwl, //
        es_inst_lwr, //
        es_inst_swl, //
        es_inst_swr, //
        es_load_sign, //
        es_load_byte, //
        es_load_hw,  //140
        es_store_byte, //
        es_store_hw,      //138
        es_alu_op      ,  //137:126
        es_load_op     ,  //125:125
        es_src1_is_sa  ,  //124:124
        es_src1_is_pc  ,  //123:123
        es_src1_is_0   ,  //122:122
        es_src2_is_imm ,  //121:121
        es_src2_is_0imm , //120:120
        es_src2_is_8   ,  //119:119
        es_gr_we       ,  //118:118
        es_mem_we      ,  //117:117
        es_dest        ,  //116:112
        es_imm         ,  //111:96
        es_rs_value    ,  //95 :64
        es_rt_value    ,  //63 :32
        es_pc             //31 :0
       } = ds_to_es_bus_r;

wire [31:0] es_alu_src1   ;
wire [31:0] es_alu_src2   ;
wire [31:0] es_alu_result ;

wire        es_res_from_mem;
wire        fmul;
wire        fdiv;
wire    mthi,mtlo;
wire    [31:0] hilo;

reg    [3:0]store_wen;
wire   [31:0]st_data;
wire   [31:0]swl_data;
wire   [31:0]swr_data;

wire    overflow;
wire    adel;
wire    ades;
wire    tlbm;
wire    tlbl;
wire    tlbs;
wire   [31:0] badvaddr;

assign es_res_from_mem = es_load_op;
assign es_to_ms_bus = { es_refetch, //231
                        es_tlb_r,   //230 
                        es_inst_tlbwi,//229
                        es_inst_tlbr,//228
                        es_mem_we,   //227:227
                        badvaddr,   //226:195
                        es_c0_waddr, //194:190
                        es_bd,   //189
                        es_ex      ,   //188
                        es_excode   , //187:183
                        es_inst_mfc0, //182
                        es_inst_mtc0,
                        es_inst_eret,//180
                        es_inst_lwl,
                        es_inst_lwr,
                        es_load_sign,
                        es_load_byte,
                        es_load_hw,
                        es_store_byte,
                        es_store_hw,   //179: 173
                        mthi,
                        mtlo,
                        inst_mfhi,
                        inst_mflo,
                        hilo,
                        fmul,
                        fdiv,       //  172: 135
                        divouthi,   //  134: 103
                        divoutlo,    // 102:71
                        es_res_from_mem,  //70:70
                        es_gr_we       ,  //69:69
                        es_dest        ,  //68:64
                        es_alu_result  ,  //63:32
                        es_pc             //31:0
                      };
assign es_to_ds_bus = {es_inst_mfc0   , //38:38
                       es_load_op     , //37:37
                       es_dest        ,  //36:32
                       es_alu_result    //31:0
                      };

//exception
assign es_ex=(ds_ex | overflow | adel | ades | tlbm | tlbs | tlbl ) & es_valid ;
assign es_excode = (ds_ex&(ds_excode == 5'h00)) ? ds_excode : //中断优先级最高
                   (tlbm)?5'h01:
                   (tlbl)?5'h02:
                   (tlbs)?5'h03:
                   (overflow) ? 5'h0c : //ex阶段指令优先(先执行的指令产生例外)
                   (adel) ? 5'h04 :
                   (ades) ? 5'h05 :
                   ds_excode;
assign es_tlb_r = ((es_mem_we | (es_load_op&~inst_mfhi & ~inst_mflo))&~tlb_found) | ds_tlb_r;

assign mthi = inst_mthi;
assign mtlo = inst_mtlo;
assign hilo = es_rs_value; 
assign fmul = inst_mult | inst_multu;
assign fdiv = inst_div | inst_divu;

assign addr_4 = es_alu_result[1:0];

assign es_ready_go    = (data_sram_req & ~es_ex)?
((data_sram_addr_ok & ~flush)?1'b1:1'b0):
(ms_inst_mtc0 & op_tlbp)?1'b0:
(inst_div | inst_divu) ? divcomplete : 1'b1;

assign es_allowin     = !es_valid || es_ready_go && ms_allowin;
assign es_to_ms_valid =  es_valid && es_ready_go;
assign es_to_ds_valid =  es_valid && es_gr_we;

assign overflow = (pre_overflow)&( (es_alu_op[0]& ( (~es_alu_src1[31] & ~es_alu_src2[31] &  es_alu_result[31])
                                                        ||( es_alu_src1[31] &  es_alu_src2[31] & ~es_alu_result[31]) ) )
                                       ||(es_alu_op[1]&( (~es_alu_src1[31] &  es_alu_src2[31] &  es_alu_result[31])
                                                        ||( es_alu_src1[31] & ~es_alu_src2[31] & ~es_alu_result[31]) ) )
                                           );

assign tlbm=(tlb_found & tlb_valid & ~tlb_dirty) & ex_store;

assign tlbl=(~tlb_valid|~tlb_found) & (es_load_op&~inst_mfhi & ~inst_mflo);

assign tlbs=(~tlb_valid|~tlb_found) & ex_store;

assign adel = (inst_lw)            ? ~(es_alu_result[1:0] == 2'b00) :
              (inst_lh | inst_lhu) ? ~(es_alu_result[0] == 1'b0) :
              0;
assign ades = (inst_sw)            ? ~(es_alu_result[1:0] == 2'b00) :
              (inst_sh)  ? ~(es_alu_result[0] == 1'b0) :
              0;         

assign badvaddr = (fs_badvaddr != 0) ? fs_badvaddr : es_alu_result;               
always @(posedge clk) begin
if (reset|flush) begin
        es_valid <= 1'b0;
    end
    else if (es_allowin) begin
        es_valid <= ds_to_es_valid;
    end

    if (ds_to_es_valid && es_allowin) begin
        ds_to_es_bus_r <= ds_to_es_bus;
    end
end

assign es_alu_src1 = es_src1_is_sa  ? {27'b0, es_imm[10:6]} : 
                     es_src1_is_pc  ? es_pc[31:0] :
                     es_src1_is_0   ? 32'b0:
                                      es_rs_value;
assign es_alu_src2 = es_src2_is_imm ? {{16{es_imm[15]}}, es_imm[15:0]} : 
                      es_src2_is_0imm ? {16'b0, es_imm[15:0]} :
                     es_src2_is_8   ? 32'd8 :
                                      es_rt_value;

alu u_alu(
    .alu_op     (es_alu_op    ),
    .alu_src1   (es_alu_src1  ), //bug3
    .alu_src2   (es_alu_src2  ),
    .alu_result (es_alu_result)
    );





div  div(
    .div_clk (clk),
    .resetn  (~reset),
    .div     (inst_divu|inst_div),
    .div_signed(inst_div),
    .x      (es_rs_value),
    .y      (es_rt_value),
    .s      (divoutlo),
    .r      (divouthi),
    .complete(divcomplete)
    );

signed_multiplier  multi(
    .mul_clk(clk),
    .resetn(~reset),
    .mul_signed(inst_mult),
    .x(es_rs_value),
    .y(es_rt_value),
    .result(mulresult)
    );
always @(*)begin
    if(es_store_byte) begin
        store_wen = (addr_4==2'b00)?4'b0001:
                    (addr_4==2'b01)?4'b0010:
                    (addr_4==2'b10)?4'b0100:
                    /*(addr_4==2'b11)?*/4'b1000;
    end
    else if (es_store_hw)begin
        store_wen = (addr_4==2'b00)?4'b0011:
                    /*(addr_4==2'b10)*/4'b1100;
    end
    else if (es_inst_swl)begin
        store_wen = (addr_4==2'b00)?4'b0001:
                    (addr_4==2'b01)?4'b0011:
                    (addr_4==2'b10)?4'b0111:
                    /*(addr_4==2'b11)?*/4'b1111;
    end
    else if (es_inst_swr)begin
        store_wen = (addr_4==2'b00)?4'b1111:
                    (addr_4==2'b01)?4'b1110:
                    (addr_4==2'b10)?4'b1100:
                    /*(addr_4==2'b11)?*/4'b1000;
    end
    else begin
        store_wen = 4'hf;
    end
end

assign st_data = es_store_byte?{4{es_rt_value[7:0]}} :
                es_store_hw?{2{es_rt_value[15:0]}} :
                es_rt_value ;
assign swl_data =   (addr_4==2'b00)?{27'b0,es_rt_value[31:24]}:
                    (addr_4==2'b01)?{16'b0,es_rt_value[31:16]}:
                    (addr_4==2'b10)?{8'b0,es_rt_value[31:8]}:
                    /*(addr_4==2'b11)?*/es_rt_value;
assign swr_data =   (addr_4==2'b00)?es_rt_value:
                    (addr_4==2'b01)?{es_rt_value[23:0],8'b0}:
                    (addr_4==2'b10)?{es_rt_value[15:0],16'b0}:
                    /*(addr_4==2'b11)?*/{es_rt_value[7:0],24'b0};

assign ex_store=es_mem_we ;

assign data_sram_req   = es_valid & ms_allowin & ((es_mem_we & ~ms_flush & ~es_ex & ~flush)| (es_load_op&~inst_mfhi & ~inst_mflo));
assign data_sram_wr    = (es_load_op)?1'b0:1'b1;
assign data_sram_size  = (es_load_byte|es_store_byte) ? 2'b00:
                         (es_load_hw|es_store_hw)     ? 2'b01:
                         (es_inst_lwl|es_inst_swl)    ?
                                ((addr_4==2'b00) ? 2'b00 :
                                 (addr_4==2'b01) ? 2'b01 : 2'b10) :
                         (es_inst_lwr|es_inst_swr)    ?
                                ((addr_4==2'b11) ? 2'b00 :
                                 (addr_4==2'b10) ? 2'b01 : 2'b10) : 2'b10  ;
assign data_sram_wstrb = store_wen ;
assign data_sram_addr  = (es_inst_lwl|es_inst_swl)?{es_alu_result[31:2],2'b00}:es_alu_result;
assign data_sram_wdata = (es_inst_swl)?swl_data:
                        (es_inst_swr)?swr_data:
                                        st_data;

endmodule
